/* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is confidential property of Nordic
 * Semiconductor ASA.Terms and conditions of usage are described in detail
 * in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 */

/** @file
 * @brief Example project on RAM retention
 * @defgroup ram_retention_example RAM retention example
 * @{
 * @ingroup nrf_examples_nrf6310
 *
 * @brief Example usage and verification of RAM retention mode-
 *
 * This example configures the system for ram retention and the triggers a system off. After waking up
 * through gpio pin number 7 (active low) this code checks of the RAM state is conserved by checking the
 * value written before going to system off.
 * @note This example run in a loop @ref MAX_TEST_ITERATIONS number of times and the loop is generated by a
 *       System reset which is generated after wakeup from SYSTEM-OFF mode, does not work in evaluation mode
 *       of Keil MDK as scatter files only work in full licensed version. Scatter files are needed for telling
 *       the ARM linker whether to initialize RAM or not at system reset. This example specially is designed to
 *       run without linker scripts because the RAM_MEMORY_TEST_ADDRESS is chosen to be not used by any variable
 *       and hence no linker will try to initialize it at start-up.
 *
 * @verbatim
 *                             --------------
 *                            |    RESET     |
 *                             --------------
 *                                   |
 *                             ---------------
 *                            |   Configure   |
 *                            |     Gpio      |
 *                             ---------------
 *                                   |
 *                         READ GPREGRET Register
 *                                   |
 *                         ---------------------
 *                 NO  <--|  SYSTEM_OFF_RESET?  |--> YES
 *                 |       ---------------------      |
 *                 |                                  |
 *       -------------------------           ---------------------
 *      |   write to GPREGRET     |         |   Read Loop count/  |
 *      | test word +  loop_count |<-    ---|   MAX reached?      |--->NO
 *       -------------------------   |   |   ---------------------     |
 *                 |                 |   |                 ------------------------               ----------------
 *       ------------------------    |  YES               |  Verify RAM retention? |-->FAILURE-->|  Loop forever  |
 *      |  Enable RAM RETENTION  |   |   |                 ------------------------               ----------------
 *       ------------------------    | STOP                           |                                   ^
 *                 |                 |                             SUCCESS                                |
 *       ------------------------    |          -------------         |                                   |
 *      | WRITE_TEST_BYTE_TO_RAM |    --True---| ++Loop < 6? |<--------                                   |
 *       ------------------------               -------------                                             |
 *                 |                                 |                                                    |
 *        --------------------                     False                                                  |
 *       | trigger System Off |                      |                                                    |
 *        --------------------                        ----------------------------------------------------
 * @endverbatim
 * @image html example_board_setup_a.png "Use board setup A for this example."
 */

#include <stdbool.h>
#include <stdint.h>
#include "nrf_delay.h"
#include "nrf_gpio.h"

#define RAM_MEMORY_TEST_ADDRESS     (0x20002000UL)        //!< Address in RAM where test word (RAM_MEMORY_TEST_WORD) is written before system off and checked after system reset
#define RAM_MEMORY_TEST_WORD        (0xFEEDBEEFUL)        //!< Test word that is written to RAM address RAM_MEMORY_TEST_ADDRESS
#define RESET_MEMORY_TEST_BYTE      (0x0DUL)              //!< known sequence written to special register to check if this wake up from system off.
#define MAX_TEST_ITERATIONS         (2)                   //!< maximum numer of iterations this example will run
#define SUCCESS_OUTPUT_VALUE        (0xAB)                //!< If ram retention is tested MAX_TEST_ITERATIONS, this value will be given as output which can be used by tester
#define PIN_GPIO_WAKEUP             (7)                   //!< Gpio pin configured to wakeup system from system off on falling edge. Can be connected to a button which is high when not pressed

/*
 * Output in HardFault_Handler in case something went wrong
 * or system off did not work and we reached the end of the program
 */
void HardFault_Handler()
{
  NRF_GPIO->OUT = 0;

  while(true)
  {
    NRF_GPIO->OUT ^= (1UL << 8);
    nrf_delay_ms(100);
  }
}
/*
 *  Configures pin 8-16 as outputs and pin 7 as input with wake up capabilies from system off.
 */
static void gpio_config(void)
{
  // Set Port 1 as output
  nrf_gpio_range_cfg_output(8, 15);

  // This pin is used for waking up from system off and is active low, so enable sense capabilities
  nrf_gpio_cfg_input(PIN_GPIO_WAKEUP, NRF_GPIO_PIN_PULLUP);
  NRF_GPIO->PIN_CNF[PIN_GPIO_WAKEUP] |= (GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos);
}

/*
 * Sets all the output pins to show failure and loop forever
 */
static void display_failure(void)
{
  nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, 0XFF);

  // loop forever
  while(true)
  {
  }
}

/**
 * main() function
 * @return 0. int return type required by ANSI/ISO standard.
 */
int main(void)
{
  uint32_t *p_ram_test = (uint32_t *)RAM_MEMORY_TEST_ADDRESS;
  uint8_t loop_count = 0;

  // Configure pins 8-15 as outputs, and pin 7 as input (input with sense capabilities that can wake
  // up the system from OFF mode)
  gpio_config();

  // This delay is needed because the debugger (and only needed if using any debugger) might reset the
  // system multiple times when flashing in which case GPREGRET register is programmed incorrectly in
  // between two resets and when the flashing is finished then this example will assume that the system
  // woke up from system off (bad GPREGRET content) and returns error when it does not find the test byte
  // in RAM. This delay will give enough time for multiple resets by the debugger and still not programming
  // GPREGRET until flashing is finished. Delay time might change with debugger
  nrf_delay_ms(100);

  
  // Check if we just woke up from SYSTEM OFF mode by checking NRF_POWER->GPREGRET register which has
  // retained value which was written before going to system off below is the layout for usage for
  // NRF_POWER->GPREGRET register.
  //  BITS |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0
  //  --------------------------------------------------------------------
  //       |        SPECIAL_SEQUENCE       |          LOOP_COUNT
  //  --------------------------------------------------------------------
  ///
  if ((NRF_POWER->GPREGRET >> 4) == RESET_MEMORY_TEST_BYTE)
  {
    // Take the loop_count value
    loop_count = (uint8_t)(NRF_POWER->GPREGRET & 0xFUL);
    NRF_POWER->GPREGRET = 0;

    if(loop_count >= (uint8_t)MAX_TEST_ITERATIONS)
    {
      nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, SUCCESS_OUTPUT_VALUE);
      while(true)
      {
      }
    }

    if(*p_ram_test != RAM_MEMORY_TEST_WORD )
    {
      display_failure();
    }
    else
    {
      // Note that this loop is just to display that we have verified ram retention after waking
      // up from system off, not needed in real application
      for(uint32_t i = 8; i > 0; i--)
      {
        nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, (uint8_t)(0X01 << i));
        nrf_delay_ms(50);
      }
    }
    *p_ram_test = 0;
  }

  // Write known sequence + loop_count to GPREGRET register
  loop_count++;
  NRF_POWER->GPREGRET = ( (RESET_MEMORY_TEST_BYTE << 4) | loop_count );

  // Write known value to known address in ram, enable ram retention and set system off and wait
  // for gpio wakeup from external device
  // Light up LED telling that we are preparing for system off with ram retention enabled
  // Note that this loop is just to display that we are preparing for system off, not needed in real
  // application
  for(uint8_t i = 0; i < 8; i++)
  {
    nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, (uint8_t)(0X01 << i));
    nrf_delay_ms(50);
  }

  // Switch ON both RAM banks when in system OFF mode
  NRF_POWER->RAMON |= (POWER_RAMON_OFFRAM0_RAM0On << POWER_RAMON_OFFRAM0_Pos) |
          (POWER_RAMON_OFFRAM1_RAM1On << POWER_RAMON_OFFRAM1_Pos);

  // Write test word to RAM memory
  *p_ram_test = RAM_MEMORY_TEST_WORD;

  // Enter system off and wait for wake up from gpio detect signal
  NRF_POWER->SYSTEMOFF = 0x1;

  // Will reach here only if system off did not work and hence will lead to a hard-fault which will 
  // be handled in HardFault_Handler(). If wake up condition is already active while system off is triggered,
  // then the system will go to system off and wakes up immediately with a system reset.
}

/**
 *@}
 **/
